#ifdef _WIN32

/////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <sstream>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include "KFile.h"
struct WorkerProcess
{
	HANDLE hProcess;
	KFile fp;
	HANDLE pipe[2];
	int pid;
	int pending_count;
	bool closed;
};
#ifndef PROGRAM_NAME
#define PROGRAM_NAME "cdnbest"
#endif
#ifndef PROGRAM
#define PROGRAM "\\" PROGRAM_NAME ".exe"
#endif
#define localtime_r( _clock, _result ) 	( *(_result) = *localtime( (_clock) ), (_result) )
#define snprintf _snprintf
std::string program;
volatile bool quit_program_flag = false;
size_t current_log_size = 0;
size_t log_rotate_size = 100*1024*1024;
char *log_file = NULL;
WorkerProcess worker_process;
SERVICE_STATUS ssStatus;
SERVICE_STATUS_HANDLE sshStatusHandle;
HANDLE shutdown_event = INVALID_HANDLE_VALUE;
HANDLE hEventSource = INVALID_HANDLE_VALUE;
void coredump(DWORD pid,HANDLE hProcess,PEXCEPTION_POINTERS pExInfo);
bool UninstallService( const char *szServiceName,bool uninstall);
void LogEvent(LPCTSTR pFormat, ...);
static void getSubKey(std::stringstream &subkey,const char *service)
{
	subkey << "SOFTWARE\\Bangteng\\" << service;
}
static void deleteRegister(const char *service)
{
	std::stringstream subkey;
	getSubKey(subkey,service);
	LONG ret = RegDeleteKey(HKEY_LOCAL_MACHINE,subkey.str().c_str());
	if(ERROR_SUCCESS != ret){
		//fprintf(stderr,"cann't delete register error=%d\n",ret);
	}
}
static void writeRegister(const char *service)
{
	std::stringstream subkey;
	getSubKey(subkey,service);
	HKEY sk;
	DWORD disposition;
	LONG ret = RegCreateKeyEx(HKEY_LOCAL_MACHINE,subkey.str().c_str(),0,NULL,0,KEY_WRITE,NULL,&sk,&disposition);
	if(ERROR_SUCCESS != ret){
		fprintf(stderr,"cann't write register error=%d\n",ret);
		return;
	}
	RegCloseKey(sk);
}
bool InstallService(const char *szServiceName,bool install=true,bool start=true)
{
	UninstallService(szServiceName,true);
	SC_HANDLE handle = ::OpenSCManager(NULL, NULL,SC_MANAGER_ALL_ACCESS);
	bool result = true;
	if(handle==NULL) {
		int error = GetLastError();
		fprintf(stderr,"cann't open sc manager errno=%d\n",error);
		return false;
	}
	SC_HANDLE hService = NULL;
	if (install) {
		char szFilename[256];
		::GetModuleFileName(NULL, szFilename, 255);
		strcat(szFilename," --ntsrv");
		hService = ::CreateService(handle, szServiceName,
				szServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
				SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, szFilename, NULL,
				NULL, NULL, NULL, NULL);
		if(hService==NULL){
			int error = GetLastError();
			fprintf(stderr,"cann't create service manager errno=%d\n",error);
			return false;
		}
		writeRegister(szServiceName);
		SERVICE_DESCRIPTION tmp;
		memset(szFilename,0,sizeof(szFilename));
		_snprintf(szFilename,255,"%s", szServiceName);
		tmp.lpDescription=szFilename;
		ChangeServiceConfig2(hService,SERVICE_CONFIG_DESCRIPTION,&tmp);
	} else {
		hService= ::OpenService(handle,szServiceName,SERVICE_ALL_ACCESS);
	}
	if(start){
		char *arg[]={(char *)"--ntsrv",NULL};
		result = StartService(hService,NULL,(LPCSTR *)arg) == TRUE ;
	}
	::CloseServiceHandle(hService);
	::CloseServiceHandle(handle);	
	return result;
}
static const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
void CTIME_R(time_t *a, char *b, size_t l) {
#if	defined(HAVE_CTIME_R)
#if	defined(CTIME_R_3)
	ctime_r(a, b, l);
#else
	ctime_r(a, b);
#endif /* SOLARIS */
#else
	struct tm tm;
	memset(b, 0, l);
	localtime_r(a, &tm);
	_snprintf(b, l - 1, "%s %02d %s %02d:%02d:%02d\n", days[tm.tm_wday],
			tm.tm_mday, months[tm.tm_mon], tm.tm_hour, tm.tm_min, tm.tm_sec);
#endif /* HAVE_CTIME_R */
}
bool open_log_file() {
	if (log_file) {
		if (worker_process.fp.open(log_file,fileAppend)) {
			current_log_size = (size_t)(worker_process.fp.getFileSize());
			//printf("current_log_size=[%d]\n",current_log_size);
			return true;
		}
	}
	return false;
}
void rotate_log(int level) {
	if (log_file) {
		int len = strlen(log_file);
		int new_len = len + 8;
		char *old_file = (char *)malloc(new_len);
		memset(old_file,0,new_len);
		if (level>0) {
			snprintf(old_file,len+8,"%s.%d",log_file,level);
		} else {
			snprintf(old_file,len+8,"%s",log_file);
		}
		if (level >= 10) {
			unlink(old_file);
			free(old_file);
			return;
		}
		struct _stat buf;
		if (_stat(old_file, &buf) == 0) {
			rotate_log(level + 1);
		}
		char *new_file = (char *)malloc(new_len);
		memset(new_file,0,new_len);
		snprintf(new_file,len+8,"%s.%d",log_file,level+1);
		rename(old_file, new_file);
		free(old_file);
		free(new_file);
	}
}
void write_log_time()
{
	if (log_rotate_size>0 && current_log_size>log_rotate_size) {
		worker_process.fp.close();
		rotate_log(0);
		open_log_file();
	}
	time_t ltime;
	time(&ltime);
	char tm[30];
	CTIME_R(&ltime, tm, sizeof(tm));
	tm[19] = 0;
	worker_process.fp.write(tm,19);
	worker_process.fp.write("|",1);
	current_log_size += 20;
}
void write_log_msg(char *msg,int len) {
	len = worker_process.fp.write(msg,len);
	if (len>0) {
		current_log_size+=len;
	}
}
void write_log(char *msg,int len) {
	//if (log_pid<0) {
	//	return write(1,msg,len);
	//}
	while (len>0) {
		char *line = (char *)memchr(msg,'\n',len);
		if (line!=NULL) {
			int line_len = line - msg + 1;
			write_log_msg(msg,line_len);
			write_log_time();
			msg += line_len;
			len -= line_len;
			continue;			
		} else {
			write_log_msg(msg,len);
			break;
		}
	}
}
SERVICE_STATUS servicestatus;
SERVICE_STATUS_HANDLE servicestatushandle;
bool init_worker_process(WorkerProcess *process) {
	if (process->pipe[0]!=INVALID_HANDLE_VALUE) {
		CloseHandle(process->pipe[0]);
		process->pipe[0] = INVALID_HANDLE_VALUE;
	}
	if (process->pipe[1]!=INVALID_HANDLE_VALUE) {
		CloseHandle(process->pipe[1]);
		process->pipe[1] = INVALID_HANDLE_VALUE;
	}
	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;
	if (!CreatePipe(&process->pipe[0],&process->pipe[1],&sa,0)) {
		return false;
	}
	return true;
}
void create_worker_process(WorkerProcess *process)
{
	char path[512];
	path[0] = '\0';
	::GetModuleFileName(NULL, path, 511);
	char *p = strrchr(path,'\\');
	if (p==NULL || !init_worker_process(process)) {
		return;
	}
	*p = '\0';
	if (log_file) {
		free(log_file);
		log_file = NULL;
	}
	log_file = (char *)malloc(strlen(path)+32);
	strcpy(log_file,path);
	strcpy(log_file + strlen(path),"\\..\\var\\daemon.log");
	strcat(p,PROGRAM);
	if (!open_log_file()) {
		free(log_file);
		log_file = NULL;
	}
	PROCESS_INFORMATION pi;
	STARTUPINFO si;
	BOOL bResult = FALSE;
	DWORD code;
	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb= sizeof(STARTUPINFO);
	si.lpDesktop = TEXT("winsta0\\default");
	si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
	si.wShowWindow = SW_HIDE;
	si.hStdInput = INVALID_HANDLE_VALUE;
	si.hStdOutput = process->pipe[1];
	si.hStdError = process->pipe[1];
	bResult = CreateProcess(
	  NULL,              // file to execute
	  path,     // command line
	  NULL,              // pointer to process SECURITY_ATTRIBUTES
	  NULL,              // pointer to thread SECURITY_ATTRIBUTES
	  TRUE,             // handles are not inheritable
	  DETACHED_PROCESS | CREATE_NO_WINDOW ,   // creation flags,   // creation flags
	  NULL,              // pointer to new environment block 
	  NULL,              // name of current directory 
	  &si,               // pointer to STARTUPINFO structure
	  &pi                // receives information about new process
	);
	if (!bResult) {
		code = GetLastError();
		LogEvent("cann't create process [%s],error=%d\n",path,code);
		Sleep(2000);
		return;
	}
	LogEvent("create worker process [%s] success with pid=%d\n",path,pi.dwProcessId);
	process->hProcess = pi.hProcess;
	process->pid = pi.dwProcessId;
	CloseHandle(pi.hThread);
	if (process->pipe[1]!=INVALID_HANDLE_VALUE) {
		CloseHandle(process->pipe[1]);
		process->pipe[1] = INVALID_HANDLE_VALUE;
	}
	process->closed = false;
	process->pending_count = 0;
}
void start_safe_service()
{
	//init_safe_process();
	DWORD code;
	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = FALSE;
	shutdown_event = CreateEvent(&sa,TRUE,FALSE,NULL);
	if (shutdown_event==INVALID_HANDLE_VALUE) {
		code = GetLastError();
		LogEvent("cann't create shutdown event,error=%d",code);
		exit(0);
	}
	sa.bInheritHandle = TRUE;
	worker_process.closed = true;
	worker_process.pipe[0] = worker_process.pipe[1] = INVALID_HANDLE_VALUE;
	bool create_process_sleep = false;

	HANDLE ev[2];
	char buf[512];
	for (;;) {
		if (quit_program_flag) {
			SetEvent(shutdown_event);
			break;
		}
		if (worker_process.closed) {
			if (create_process_sleep) {
				Sleep(2000);
			}
			create_worker_process(&worker_process);				
		}
		for (;;) {
				unsigned long len = 0;
				if(!ReadFile(worker_process.pipe[0],buf,sizeof(buf)-1,&len,NULL)) {
					break;
				}
				if (len<=0) {
					break;
				}
				write_log(buf,len);
		}
	
		int ret = WaitForSingleObject(worker_process.hProcess,2000);
		if (ret==WAIT_OBJECT_0) {
			if (GetExitCodeProcess(worker_process.hProcess,&code)) {
				LogEvent("worker process exit code=%d",code);
				if (code==0) {
					SetEvent(shutdown_event);
					break;
				}
			} else {
				LogEvent("Cann't get worker process exit code");
			}
			CloseHandle(worker_process.hProcess);
			worker_process.closed = true;
			continue;
		}
		if (ret==WAIT_OBJECT_0+1) {
			break;
		}
	}

}
void stop_safe_service()
{	
	quit_program_flag = true;	
	TerminateProcess(worker_process.hProcess,0);	
	WaitForSingleObject(shutdown_event,INFINITE);
}
void WINAPI ServiceCtrlHandler(DWORD dwControl)
{
	switch (dwControl)
	{
		case SERVICE_CONTROL_PAUSE:
		servicestatus.dwCurrentState = SERVICE_PAUSE_PENDING;
		//
		// This value need to try a lot to confirm
		// ...
		::SetServiceStatus(servicestatushandle, &servicestatus);

		// not called in this service
		// ...
		servicestatus.dwCurrentState = SERVICE_PAUSED;
		//
		break;

		case SERVICE_CONTROL_CONTINUE:
		servicestatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
		::SetServiceStatus(servicestatushandle, &servicestatus);
		servicestatus.dwCurrentState = SERVICE_RUNNING;
		//
		break;

		case SERVICE_CONTROL_STOP:
		servicestatus.dwCurrentState = SERVICE_STOP_PENDING;
		//
		::SetServiceStatus(servicestatushandle, &servicestatus);
		//
		stop_safe_service();
		servicestatus.dwCurrentState = SERVICE_STOPPED;
		//
		break;

		case SERVICE_CONTROL_SHUTDOWN:
		servicestatus.dwCurrentState = SERVICE_STOP_PENDING;
		::SetServiceStatus(servicestatushandle, &servicestatus);
		stop_safe_service();

		servicestatus.dwCurrentState = SERVICE_STOPPED;

		break;

		case SERVICE_CONTROL_INTERROGATE:
		servicestatus.dwCurrentState = SERVICE_RUNNING;
		break;
	}
	::SetServiceStatus(servicestatushandle, &servicestatus);
}

void WINAPI serviceMain(DWORD argc, LPTSTR *argv)
{
	servicestatus.dwServiceType = SERVICE_WIN32;
	servicestatus.dwCurrentState = SERVICE_START_PENDING;
	servicestatus.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_CONTROL_SHUTDOWN;
	servicestatus.dwWin32ExitCode = 0;
	servicestatus.dwServiceSpecificExitCode = 0;
	servicestatus.dwCheckPoint = 0;
	servicestatus.dwWaitHint = 0;

	servicestatushandle = ::RegisterServiceCtrlHandler(PROGRAM_NAME, ServiceCtrlHandler);
	if (servicestatushandle == (SERVICE_STATUS_HANDLE)0)
	{
		return;
	}

	bool bInitialized = false;
	// Initialize the service
	bool is_running = false;
	//parse_args(argc,argv,is_running);
	bInitialized = true;
	servicestatus.dwCheckPoint = 0;
	servicestatus.dwWaitHint = 0;
	if (!bInitialized)
	{
		servicestatus.dwCurrentState = SERVICE_STOPPED;
		servicestatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
		servicestatus.dwServiceSpecificExitCode = 1;
	}
	else
	{
		servicestatus.dwCurrentState = SERVICE_RUNNING;
	}
	::SetServiceStatus(servicestatushandle, &servicestatus);
	start_safe_service();
	return;
}

void LogEvent(LPCTSTR pFormat, ...)
{
	TCHAR chMsg[512];	
	LPTSTR lpszStrings[1];
	va_list pArg;
	va_start(pArg, pFormat);
	_vstprintf(chMsg, pFormat, pArg);
	//vprintf(pFormat, pArg);
	va_end(pArg);
	lpszStrings[0] = chMsg;
	if(hEventSource == INVALID_HANDLE_VALUE){
		hEventSource = RegisterEventSource(NULL, PROGRAM_NAME);
	}
	if (hEventSource != INVALID_HANDLE_VALUE)
	{
		ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
	}
}
bool UninstallService( const char *szServiceName,bool uninstall=true)
{
	bool result = true;
	SC_HANDLE handle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	SC_HANDLE hService= ::OpenService(handle,szServiceName,SERVICE_ALL_ACCESS);
	SERVICE_STATUS service_status;
	result = ControlService(hService,SERVICE_CONTROL_STOP,&service_status) == TRUE;
	if (uninstall) {
		result=::DeleteService(hService)==TRUE;
	}
	::CloseServiceHandle(hService);
	::CloseServiceHandle(handle);
	if (uninstall) {
		deleteRegister(szServiceName);
	}
	return result;
}
int main(int argc,char **argv)
{
	if (argc<=1) {
		printf("Usage: %s --install|--uninstall\n",argv[0]);
		return 1;
	}
	if (strcmp(argv[1],"--onlyinstall")==0) {
		if(InstallService(PROGRAM_NAME,true,false)) {
			printf("install service success\n");
			return 0;
		} else {
			printf("install service failed\n");
			return 1;
		}
	}
	if (strcmp(argv[1],"--start")==0) {
		if(InstallService(PROGRAM_NAME,false,true)) {
			printf("start service success\n");
			return 0;
		} else {
			printf("start service failed\n");
			return 1;
		}
	}
	if (::strcmp(argv[1], "--stop")==0) {
		if(UninstallService(PROGRAM_NAME,false)) {
			printf("stop service success\n");
			return 0;
		} else {
			printf("stop service failed\n");
			return 1;
		}
	}
	if (::strcmp(argv[1], "--install")==0) {
		if (InstallService(PROGRAM_NAME)) {
			printf("install service success\n");
		} else {
			printf("install service failed\n");
		}
		return 0;
	}
	if (::strcmp(argv[1], "--uninstall")==0) {
		if (UninstallService(PROGRAM_NAME)) {
			printf("uninstall service success\n");
		} else {
			printf("uninstall service failed\n");
		}
		return 0;
	}
	if(::strcmp(argv[1], "--ntsrv")==0) {
		SERVICE_TABLE_ENTRY service_table_entry[] =
		{
			{	PROGRAM_NAME, serviceMain},
			{	NULL, NULL}
		};
		::StartServiceCtrlDispatcher(service_table_entry);
		return 0;
	}
	if (::strcmp(argv[1], "--safe")==0) {
		start_safe_service();
	}
	return 0;
}
#endif
